/*-------------------------------------------------------------------------
 *
 * nodeSeqscan.c
 *	  支持关系的顺序扫描的例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeSeqscan.c
 *
 *-------------------------------------------------------------------------
 */
/*
 * 接口例程
 *		ExecSeqScan				顺序扫描关系。
 *		ExecSeqNext				按顺序检索下一个元组。
 *		ExecInitSeqScan			创建并初始化 seqscan 节点。
 *		ExecEndSeqScan			释放任何分配的存储。
 *		ExecReScanSeqScan		重新扫描关系
 *
 *		ExecSeqScanEstimate		估算并行扫描所需的 DSM 空间
 *		ExecSeqScanInitializeDSM 初始化并行扫描的 DSM
 *		ExecSeqScanReInitializeDSM 重新初始化 DSM 以进行新的并行扫描
 *		ExecSeqScanInitializeWorker 附加到并行工作线程中的 DSM 信息
 */
#include "postgres.h"

#include "access/relscan.h"
#include "access/tableam.h"
#include "executor/execdebug.h"
#include "executor/nodeSeqscan.h"
#include "utils/rel.h"

static TupleTableSlot *fc_SeqNext(SeqScanState *fc_node);

/* ----------------------------------------------------------------
 *						扫描支持
 * ----------------------------------------------------------------
 */

/* ----------------------------------------------------------------
 *		SeqNext
 *
 *		这是 ExecSeqScan 的主力。
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_SeqNext(SeqScanState *fc_node)
{
	TableScanDesc fc_scandesc;
	EState	   *fc_estate;
	ScanDirection fc_direction;
	TupleTableSlot *fc_slot;

	/*
	 * 从estate和扫描状态获取信息
	 */
	fc_scandesc = fc_node->ss.ss_currentScanDesc;
	fc_estate = fc_node->ss.ps.state;
	fc_direction = fc_estate->es_direction;
	fc_slot = fc_node->ss.ss_ScanTupleSlot;

	if (fc_scandesc == NULL)
	{
		/*
		 * 如果扫描不是并行的，或者我们正在串行执行一个计划为并行的扫描，我们会到这里。
		 */
		fc_scandesc = table_beginscan(fc_node->ss.ss_currentRelation,
								   fc_estate->es_snapshot,
								   0, NULL);
		fc_node->ss.ss_currentScanDesc = fc_scandesc;
	}

	/*
	 * 从表中获取下一个元组
	 */
	if (table_scan_getnextslot(fc_scandesc, fc_direction, fc_slot))
		return fc_slot;
	return NULL;
}

/*
 * SeqRecheck -- 访问方法例程以在 EvalPlanQual 中重新检查元组
 */
static bool fc_SeqRecheck(SeqScanState *fc_node, TupleTableSlot *fc_slot)
{
	/*
	 * 请注意，与 IndexScan 不同，SeqScan 从不使用 heap_beginscan 中的键
	 * （这非常糟糕） - 因此，在这里我们不检查键是否正常。
	 */
	return true;
}

/* ----------------------------------------------------------------
 *		ExecSeqScan(node)
 *
 *		顺序扫描关系并返回下一个符合条件的元组。
 *		我们调用 ExecScan() 例程并传递适当的
 *		访问方法函数。
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecSeqScan(PlanState *fc_pstate)
{
	SeqScanState *fc_node = castNode(SeqScanState, fc_pstate);

	return ExecScan(&fc_node->ss,
					(ExecScanAccessMtd) fc_SeqNext,
					(ExecScanRecheckMtd) fc_SeqRecheck);
}


/* ----------------------------------------------------------------
 *		ExecInitSeqScan
 * ----------------------------------------------------------------
 */
SeqScanState *
ExecInitSeqScan(SeqScan *fc_node, EState *fc_estate, int fc_eflags)
{
	SeqScanState *fc_scanstate;

	/*
	 * 从前曾可以有 SeqScan 的 outerPlan，但现在不行了。
	 */
	Assert(outerPlan(fc_node) == NULL);
	Assert(innerPlan(fc_node) == NULL);

	/*
	 * 创建状态结构
	 */
	fc_scanstate = makeNode(SeqScanState);
	fc_scanstate->ss.ps.plan = (Plan *) fc_node;
	fc_scanstate->ss.ps.state = fc_estate;
	fc_scanstate->ss.ps.ExecProcNode = fc_ExecSeqScan;

	/*
	 * 杂项初始化
	 *
	 * 为节点创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_scanstate->ss.ps);

	/*
	 * 打开扫描关系
	 */
	fc_scanstate->ss.ss_currentRelation =
		ExecOpenScanRelation(fc_estate,
							 fc_node->scan.scanrelid,
							 fc_eflags);

	/* 并使用适当的行类型创建槽 */
	ExecInitScanTupleSlot(fc_estate, &fc_scanstate->ss,
						  RelationGetDescr(fc_scanstate->ss.ss_currentRelation),
						  table_slot_callbacks(fc_scanstate->ss.ss_currentRelation));

	/*
	 * 初始化结果类型和投影。
	 */
	ExecInitResultTypeTL(&fc_scanstate->ss.ps);
	ExecAssignScanProjectionInfo(&fc_scanstate->ss);

	/*
	 * 初始化子表达式
	 */
	fc_scanstate->ss.ps.qual =
		ExecInitQual(fc_node->scan.plan.qual, (PlanState *) fc_scanstate);

	return fc_scanstate;
}

/* ----------------------------------------------------------------
 *		ExecEndSeqScan
 *
 *		释放通过 C 例程分配的任何存储。
 * ----------------------------------------------------------------
 */
void ExecEndSeqScan(SeqScanState *fc_node)
{
	TableScanDesc fc_scanDesc;

	/*
	 * 从节点获取信息
	 */
	fc_scanDesc = fc_node->ss.ss_currentScanDesc;

	/*
	 * 释放表达式上下文
	 */
	ExecFreeExprContext(&fc_node->ss.ps);

	/*
	 * 清空元组表
	 */
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);

	/*
	 * 关闭堆扫描
	 */
	if (fc_scanDesc != NULL)
		table_endscan(fc_scanDesc);
}

/* ----------------------------------------------------------------
 *						连接支持
 * ----------------------------------------------------------------
 */

/* ----------------------------------------------------------------
 *		ExecReScanSeqScan
 *
 *		重新扫描关系。
 * ----------------------------------------------------------------
 */
void ExecReScanSeqScan(SeqScanState *fc_node)
{
	TableScanDesc fc_scan;

	fc_scan = fc_node->ss.ss_currentScanDesc;

	if (fc_scan != NULL)
		table_rescan(fc_scan,		/* 扫描描述 */
					 NULL);		/* 新扫描键 */

	ExecScanReScan((ScanState *) fc_node);
}

/* ----------------------------------------------------------------
 *						并行扫描支持
 * ----------------------------------------------------------------
 */

/* ----------------------------------------------------------------
 *		ExecSeqScanEstimate
 *
 *		计算我们在并行查询 DSM 中所需的空间量，并告知 pcxt->estimator 我们的需求。
 * ----------------------------------------------------------------
 */
void ExecSeqScanEstimate(SeqScanState *fc_node,
					ParallelContext *fc_pcxt)
{
	EState	   *fc_estate = fc_node->ss.ps.state;

	fc_node->pscan_len = table_parallelscan_estimate(fc_node->ss.ss_currentRelation,
												  fc_estate->es_snapshot);
	shm_toc_estimate_chunk(&fc_pcxt->estimator, fc_node->pscan_len);
	shm_toc_estimate_keys(&fc_pcxt->estimator, 1);
}

/* ----------------------------------------------------------------
 *		ExecSeqScanInitializeDSM
 *
 *		设置并行堆扫描描述符。
 * ----------------------------------------------------------------
 */
void ExecSeqScanInitializeDSM(SeqScanState *fc_node,
						 ParallelContext *fc_pcxt)
{
	EState	   *fc_estate = fc_node->ss.ps.state;
	ParallelTableScanDesc fc_pscan;

	fc_pscan = shm_toc_allocate(fc_pcxt->toc, fc_node->pscan_len);
	table_parallelscan_initialize(fc_node->ss.ss_currentRelation,
								  fc_pscan,
								  fc_estate->es_snapshot);
	shm_toc_insert(fc_pcxt->toc, fc_node->ss.ps.plan->plan_node_id, fc_pscan);
	fc_node->ss.ss_currentScanDesc =
		table_beginscan_parallel(fc_node->ss.ss_currentRelation, fc_pscan);
}

/* ----------------------------------------------------------------
 *		ExecSeqScanReInitializeDSM
 *
 *		在开始新的扫描之前重置共享状态。
 * ----------------------------------------------------------------
 */
void ExecSeqScanReInitializeDSM(SeqScanState *fc_node,
						   ParallelContext *fc_pcxt)
{
	ParallelTableScanDesc fc_pscan;

	fc_pscan = fc_node->ss.ss_currentScanDesc->rs_parallel;
	table_parallelscan_reinitialize(fc_node->ss.ss_currentRelation, fc_pscan);
}

/* ----------------------------------------------------------------
 *		ExecSeqScanInitializeWorker
 *
 *		从 TOC 中复制相关信息到 planstate。
 * ----------------------------------------------------------------
 */
void ExecSeqScanInitializeWorker(SeqScanState *fc_node,
							ParallelWorkerContext *fc_pwcxt)
{
	ParallelTableScanDesc fc_pscan;

	fc_pscan = shm_toc_lookup(fc_pwcxt->toc, fc_node->ss.ps.plan->plan_node_id, false);
	fc_node->ss.ss_currentScanDesc =
		table_beginscan_parallel(fc_node->ss.ss_currentRelation, fc_pscan);
}
